/*
 * Copyright Notice ====================================================
 * This file contains proprietary information of Hewlett-Packard Co.
 * Copying or reproduction without prior written approval is prohibited.
 * Copyright (c) 2013 All rights reserved. =============================
 */

package com.hp.ecs.xqa.util.web;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import java.lang.*;
import com.hp.ecs.xqa.util.IoUtil;
import com.hp.ecs.xqa.util.JsonUtil;
import com.hp.ecs.xqa.util.ann.Param;
import com.hp.ecs.xqa.util.StringUtil;
import com.hp.ecs.xqa.util.ann.Ajax;
import com.hp.ecs.xqa.util.ann.Param;
import com.hp.*;

public class DispatcherServlet extends HttpServlet {

	private static final String RESOURCES = "/resources";

	private static final long serialVersionUID = -5550376405740566339L;
	private static final String HOME="/Home/index";

	public void testVioaltion(HttpServletRequest req) throws IOException, ServletException{
		if(req==null){
			int b=0;
			b=b*10;
		}
		
		String uri = req.getRequestURI().substring(req.getContextPath().length());
		System.out.println(uri);
		HttpServletResponse resp=null;
		service(req,resp);
	}
	
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		Object a=null;
		if(a instanceof String){
			System.out.println(a);
		}
		String uri = req.getRequestURI().substring(req.getContextPath().length());
		if (uri.startsWith(RESOURCES)) {
			final byte[] bytes = IoUtil.read(getServletContext().getResource(uri).openStream());
			resp.getOutputStream().write(bytes);
			resp.getOutputStream().flush();
			return;
		}
		try {
			dispatch(req, resp, uri.length()<3?HOME:uri);
		} catch (IllegalAccessException e) {
			throw new ServletException(e);
		} catch (InvocationTargetException e) {
			throw new ServletException(e);
		}
	}

	private void dispatch(HttpServletRequest req, HttpServletResponse resp, String uri) throws IOException,
		IllegalAccessException, InvocationTargetException, ServletException {
		String[] path = uri.substring(1).split("/");
		Class actionClazz = getActionClazz(path[0]);
		Method method = getMethod(path[1], actionClazz);
		boolean isAjax = isAjax(method);
		Annotation[][] anns = method.getParameterAnnotations();
		final Class<?>[] parameterTypes = method.getParameterTypes();
		Object[] args = new Object[parameterTypes.length];
		int pathIndex = 2;

		for (int i = 0; i < parameterTypes.length; i++) {
			final Param param = getParam(anns[i]);
			args[i] = getValue(parameterTypes[i], param, req, pathIndex < path.length ? path[pathIndex] : null);
			if (usedPath(parameterTypes[i], param)) {
				pathIndex++;
			}
		}
		Object result = method.invoke(getBean(actionClazz), args);
		if (isAjax) {
			resp.getWriter().write(JsonUtil.serialize(result));
		} else {
			String view = (String)result;
			if (view != null) {
				if (view.charAt(0) != '/') {
					view = '/' + path[0] + '/' + view;
				}
				getServletContext().getRequestDispatcher(view + ".jsp").forward(req, resp);
			}
		}
	}

	private Object getBean(Class actionClazz) {
		WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		return context.getBean(actionClazz);
	}

	private Object getValue(Class<?> type, Param param, HttpServletRequest request, String path) {
		if (type.equals(HttpServletRequest.class)) {
			return request;
		}
		if (type.isArray()) {
			Class clazz = type.getComponentType();
			String[] values = request.getParameterValues(param.value());
			if (values == null) {
				return Array.newInstance(clazz, 0);
			}
			Object result = Array.newInstance(clazz, values.length);
			for (int i = 0; i < values.length; i++) {
				Array.set(result, i, StringUtil.convert(values[i], clazz));
			}
			return result;

		} else {
			final String value;
			if (param != null) {
				value = request.getParameter(param.value());
			} else {
				value = path;
			}
			return StringUtil.convert(value, type);
		}
	}

	private boolean usedPath(Class<?> types, Param param) {
		return !(types.equals(HttpServletRequest.class) || types.isArray() || param == null);
	}

	private Param getParam(Annotation[] annotations) {
		for (Annotation an : annotations) {
			if (an instanceof Param) {
				return (Param)an;
			}
		}
		return null;
	}

	private boolean isAjax(Method method) {
		return !method.getReturnType().equals(String.class) || method.getAnnotation(Ajax.class) != null;
	}

	private Method getMethod(String name, Class actionClazz) {
		for (Method m : actionClazz.getDeclaredMethods()) {
			if (m.getName().equals(name) && m.getModifiers()==1) {
				return m;
			}
		}
		return null;
	}

	private Class getActionClazz(String action) {
		try {
			return Class.forName("com.hp.ecs.xqa.action." + action + "Action");
		} catch (ClassNotFoundException e) {
			throw new IllegalArgumentException("No ations match with the url", e);
		}
	}
}
